package pacBiopackage;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import tools.kmer.KmerSet_binary;
import tools.kmer.kmerFunctions;
import tools.sequences.sequenceUtils;

public class PacBioCorrectionByKmers 
{
	private KmerSet_binary ks=null;
	private Random generator = new Random();
	private boolean reverseFlag=false;
	private int distance_val =0;
	private int mode=0;
	private int start_kmer_count=0;
	private PacBioErrorStatisticsContainer stat=null;
	private PacBioProbTable table=null;
	
	public PacBioCorrectionByKmers(KmerSet_binary ks,PacBioErrorStatisticsContainer stat,int mode, String table)
	{
		this.ks=ks;		
		this.stat=stat;
		this.mode=mode;
		if(!table.equals("") && mode >1)
		{
			this.table= new PacBioProbTable(table);
		}
		
	}

	private char getMismatch(int type) throws Exception
	{
		
		char correctedBase=' ';
		if(type<5) //mismatch correction
		{
			if (type==0)
			{
				correctedBase='A';
			}
			else if (type==1)
			{
				correctedBase='C';
			}
			else if (type==2)
			{
				correctedBase='G';
			}
			else if (type==3)
			{
				correctedBase='T';
			}
			else if (type==4)
			{
				correctedBase='N';
			}
		}
		else
		{
			throw new Exception("Type mismatch");
		}
		return correctedBase;
	}
	
	private char[] doubleInserts(int insertType) throws Exception
	{
		char charArray[] = new char[2];
		if(insertType==5)
		{
			charArray[0]='A';
			charArray[1]='A';
		}
		else if(insertType==6)
		{
			charArray[0]='A';
			charArray[1]='C';
		}
		else if(insertType==7)
		{
			charArray[0]='A';
			charArray[1]='G';
		}
		else if(insertType==8)
		{
			charArray[0]='A';
			charArray[1]='T';
		}
		else if(insertType==9)
		{
			charArray[0]='C';
			charArray[1]='A';
		}			
		else if(insertType==10)
		{
			charArray[0]='C';
			charArray[1]='C';
		}
		else if(insertType==11)
		{
			charArray[0]='C';
			charArray[1]='G';
		}
		else if(insertType==12)
		{
			charArray[0]='C';
			charArray[1]='T';
		}
		else if(insertType==13)
		{
			charArray[0]='G';
			charArray[1]='A';
		}
		else if(insertType==14)
		{
			charArray[0]='G';
			charArray[1]='C';
		}
		else if(insertType==15)
		{
			charArray[0]='G';
			charArray[1]='G';
		}
		else if(insertType==16)
		{
			charArray[0]='G';
			charArray[1]='T';
		}
		else if(insertType==17)
		{
			charArray[0]='T';
			charArray[1]='A';
		}
		else if(insertType==18)
		{
			charArray[0]='T';
			charArray[1]='C';
		}			
		else if(insertType==19)
		{
			charArray[0]='T';
			charArray[1]='G';
		}
		else if(insertType==20)
		{
			charArray[0]='T';
			charArray[1]='T';;
		}
		else
		{
			throw new Exception("Wrong InsertType");
		}
		return charArray;
		
	}
		
	
	private PacBioKmerHelperObject simulateNewKmer(int basePosition, int end,int type, boolean insertType, PacBioReadContainer read,StringBuilder kmer) throws Exception
	{ 
		StringBuilder newKmer = null;

		if(!insertType && type<5)
		{
			char correctedBase = getMismatch(type);
			StringBuilder corrected = new StringBuilder(kmer.substring(0,kmer.length()-1));
			corrected.append(correctedBase);
			//System.out.println("check1");
			int kmer_count=checkKmerDistribution(corrected.toString());
			
			if(basePosition+1<end && basePosition+1<read.getCorrectedSequence().length())
			{
				newKmer = new StringBuilder(kmer.substring(1,kmer.length()-1));
				newKmer.append(correctedBase);
				newKmer.append(read.getCorrectedSequence().charAt(basePosition+1));
//				if(newKmer.length()<50)
//				{
//					System.out.println(insertType+" "+ type +" "+ basePosition);
//				}	
				return new PacBioKmerHelperObject(newKmer,basePosition+1,kmer_count);
				//return new Pair<StringBuilder, Integer>(newKmer,basePosition+1);
			}
			else return new PacBioKmerHelperObject(new StringBuilder(" "),0,kmer_count);//return new Pair<StringBuilder, Integer>(new StringBuilder(" "),0);
		}
		else if(!insertType && type==5) //deletion 1
		{
			StringBuilder corrected = new StringBuilder(kmer.substring(0,kmer.length()-1));
			corrected.append(read.getCorrectedSequence().charAt(basePosition+1));
			//System.out.println("check2");
			int kmer_count=checkKmerDistribution(corrected.toString());
			
			if(basePosition+4<end && basePosition+4<read.getCorrectedSequence().length())
			{
				newKmer = new StringBuilder(kmer.substring(2,kmer.length()-1));
				newKmer.append(read.getCorrectedSequence().substring(basePosition+1,basePosition+4));
				//				if(newKmer.length()<50)
//				{
//					System.out.println(insertType+" "+ type +" "+ basePosition);
//				}
				return new PacBioKmerHelperObject(newKmer,basePosition+3,kmer_count);
				//return new Pair<StringBuilder, Integer>(newKmer,basePosition+3);
			}
			else return new PacBioKmerHelperObject(new StringBuilder(" "),0,kmer_count);//return new Pair<StringBuilder, Integer>(new StringBuilder(" "),0);
				
		}
		else if(!insertType && type==6) //deletion 2
		{
			StringBuilder corrected = new StringBuilder(kmer.substring(1,kmer.length()-1));
			corrected.append(read.getCorrectedSequence().substring(basePosition+2,basePosition+4));
			//System.out.println("check3");
			int kmer_count=checkKmerDistribution(corrected.toString());
			if(basePosition+4<end && basePosition+4<read.getCorrectedSequence().length())
			{
				newKmer = new StringBuilder(kmer.substring(2,kmer.length()-1));
				newKmer.append(read.getCorrectedSequence().substring(basePosition+2,basePosition+5));
//				if(newKmer.length()<50)
//				{
//					System.out.println(insertType+" "+ type +" "+ basePosition);
//				}
				return new PacBioKmerHelperObject(kmer,basePosition+4,kmer_count);//return new Pair<StringBuilder, Integer>(newKmer,basePosition+4);
			}
			else return new PacBioKmerHelperObject(new StringBuilder(" "),0,kmer_count);//return new Pair<StringBuilder, Integer>(new StringBuilder(" "),0);
		}
		else if(insertType && type <5) //insert 1
		{
			StringBuilder corrected = new StringBuilder(kmer.substring(2,kmer.length()-1));
			corrected.append(getMismatch(type));
			corrected.append(kmer.charAt(kmer.length()-1));
			corrected.append(read.getCorrectedSequence().charAt(basePosition+1));
			//System.out.println("check4");
			int kmer_count=checkKmerDistribution(corrected.toString());
			
			if(basePosition+2<end && basePosition+2<read.getCorrectedSequence().length() )
			{
				newKmer = new StringBuilder(kmer.substring(3,kmer.length()-1));
				newKmer.append(getMismatch(type));
				newKmer.append(kmer.charAt(kmer.length()-1));
				newKmer.append(read.getCorrectedSequence().charAt(basePosition+1));
				newKmer.append(read.getCorrectedSequence().charAt(basePosition+2)); //test TODO
//				if(newKmer.length()<50)
//				{
//					System.out.println(insertType+" "+ type +" "+ basePosition);
//				}
				return new PacBioKmerHelperObject(kmer,basePosition+2,kmer_count);//return new Pair<StringBuilder, Integer>(newKmer,basePosition+2);
			}
			else return new PacBioKmerHelperObject(new StringBuilder(" "),0,kmer_count);// return new Pair<StringBuilder, Integer>(new StringBuilder(" "),0);
		}
		else //insert 2
		{
			StringBuilder corrected = new StringBuilder(kmer.substring(3,kmer.length()-1));
			corrected.append(doubleInserts(type));
			corrected.append(kmer.charAt(kmer.length()-1));
			corrected.append(read.getCorrectedSequence().charAt(basePosition+1));
			//System.out.println("check5");
			int kmer_count=checkKmerDistribution(corrected.toString());
			
			if(basePosition+2<end && basePosition+2<read.getCorrectedSequence().length())
			{
				newKmer = new StringBuilder(kmer.substring(4,kmer.length()-1));
				newKmer.append(doubleInserts(type));
				newKmer.append(kmer.charAt(kmer.length()-1));
				newKmer.append(read.getCorrectedSequence().charAt(basePosition+1));
				newKmer.append(read.getCorrectedSequence().charAt(basePosition+2)); 
//				if(newKmer.length()<50)
//				{
//					System.out.println(insertType+" "+ type +" "+ basePosition);
//				}
				return new PacBioKmerHelperObject(kmer,basePosition+2,kmer_count);//return new Pair<StringBuilder, Integer>(newKmer,basePosition+2);
			}
			else return new PacBioKmerHelperObject(new StringBuilder(" "),0,kmer_count);//return new Pair<StringBuilder, Integer>(new StringBuilder(" "),0);
		}		
	}
	
	private void updateMap(HashMap<String,Integer> map,String sb)
	{
		if(map.containsKey(sb))
		{
			map.put(sb, map.get(sb)+1);
		}
		else
		{
			map.put(sb,1);
		}
	}
	
	private void updateMultiMap(HashMap<String, HashMap<String, Integer>> multi,String a, String b)
	{
		if(multi.get(a)==null)
		{
			multi.put(a,new HashMap<String, Integer>());
		}
		if(multi.get(a).get(b)==null)
		{
			multi.get(a).put(b,1);
		}
		else
		{
			multi.get(a).put(b, multi.get(a).get(b)+1);
		}		
	}
	
	private void updateStatistic(int updatemode,int basePosition,PacBioReadContainer read)  //TODO reverse Problem catch it
	{
		StringBuilder sb = new StringBuilder();
		int tempBeginIndex =-10;
		int tempEndIndex =-10;
		
		tempBeginIndex = basePosition-5;
		tempEndIndex = basePosition+5;
		
		if(tempBeginIndex<0)tempBeginIndex=0;
		if(tempBeginIndex>read.getOrginalSequence().length())
		{
			tempBeginIndex=read.getOrginalSequence().length()-5;
		}
		
		if(tempEndIndex>read.getOrginalSequence().length())
		{
			tempEndIndex=read.getOrginalSequence().length();
		}
		
		if(basePosition > read.getOrginalSequence().length())
		{
			basePosition = read.getOrginalSequence().length();
		}
		if(basePosition < 0 )
		{
			System.out.println("error " + basePosition);
			System.exit(1);
		}
		if(basePosition-tempBeginIndex< 0 )
		{
			System.out.println("error Index is kaputt  " + tempBeginIndex + " " + basePosition +" " + read.getOrginalSequence().length());
			System.exit(1);
			sb.append(read.getOrginalSequence().substring(tempBeginIndex, read.getOrginalSequence().length()));
		}
		else
		{	
			sb.append(read.getOrginalSequence().substring(tempBeginIndex, basePosition));
		}
		if(basePosition < read.getOrginalSequence().length())
		{
			sb.append(read.getOrginalSequence().substring(basePosition,tempEndIndex));	
		}
		
		
		if(this.reverseFlag)
		{
			//reverse complement it
			sb=sequenceUtils.reverseComplement2(sb);
		}
		
		if(updatemode==0)
		{		
			updateMap(stat.getInsertEnviourment(),sb.toString());
		}
		else if(updatemode==1)
		{
			updateMap(stat.getDeletionEnviourment(), sb.toString());
			stat.getErrors().put("del",stat.getErrors().get("del")+1);
		}
		else if(updatemode==2)
		{
			updateMap(stat.getSNPEnviourment(), sb.toString());
			stat.getErrors().put("mis", stat.getErrors().get("mis")+1);
			
			if(!this.reverseFlag)
			{
				stat.getPositionsErrors().put(basePosition, stat.getPositionsErrors().get(basePosition)+1);
			}
			else
			{
				if(stat.getPositionsErrors().get((this.distance_val-basePosition)) == null) //-1 
				{
					System.out.println("update statistics: "+ stat.getPositionsErrors().toString()+"\n"+this.distance_val+" "+basePosition +" "+ (this.distance_val-basePosition));
				}
				stat.getPositionsErrors().put((this.distance_val-basePosition), stat.getPositionsErrors().get((this.distance_val-basePosition))+1);
			}
		}
		else if(updatemode==3)
		{
			stat.getErrors().put("del",stat.getErrors().get("del")+2);
		}
		else if(updatemode==4)
		{
			stat.getErrors().put("ins",stat.getErrors().get("ins")+2);
		}
		
	}
			
	private int correctRead(int basePosition, Pair<Integer,Boolean> type,PacBioReadContainer read) throws Exception
	{
		if(!type.getSecond()) //true ->inserts false ->mismatch or deletion
		{
			if(type.getFirst() <5) // now we have to correct the read
			{
				char correctedBase = getMismatch(type.getFirst());
				if(read.getCorrectedSequence().charAt(basePosition) != correctedBase)
				{		
					System.out.println("mismatch");
					updateStatistic(2,basePosition,read);
					if(!this.reverseFlag)
					{
						
						String org = String.valueOf(read.getCorrectedSequence().charAt(basePosition));
						//stat.getMismatchErrorType().get(org).put(String.valueOf(correctedBase), stat.getMismatchErrorType().get(org).get(String.valueOf(correctedBase))+1);
						updateMultiMap(stat.getMismatchErrorType(),org,String.valueOf(correctedBase));
						
						//stat.getMismatchErrorType().get(String.valueOf(read.getOrginalSequence().charAt(basePosition))).put(String.valueOf(correctedBase), bla.get(correctedBase)+1);
						stat.getPositionsErrors2().put(basePosition,stat.getPositionsErrors2().get(basePosition)+1);
					}
					else
					{
						//complement it!
						char comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(basePosition));
						char cor_comp = sequenceUtils.complement(correctedBase);
						stat.getMismatchErrorType().get(String.valueOf(comp)).put(String.valueOf(cor_comp), stat.getMismatchErrorType().get(String.valueOf(comp)).get(String.valueOf(cor_comp))+1);
						//stat.getPositionsErrors2().put(this.distance_val-basePosition,stat.getPositionsErrors2().get(this.distance_val-basePosition)+1);
						putIntoMapSave(stat.getPositionsErrors2(),this.distance_val-basePosition);
					}
					read.getCorrectedSequence().setCharAt(basePosition, correctedBase);
					read.getChecked().put(basePosition,true);
				}
				else
				{
					System.out.println("match");
				}
				return 0;
			}
			else if(type.getFirst()==5)
			{
				read.getCorrectedSequence().deleteCharAt(basePosition);
				read.getChecked().put(basePosition,true);
				read.getChecked().put(basePosition+1,true);
				if(!this.reverseFlag)
				{
					//char bla = read.getCorrectedSequence().charAt(basePosition);
					//HashMap<String, Integer>tt=stat.getMismatchErrorType().get(String.valueOf(bla));
					stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition))).put(String.valueOf('-'), stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition))).get("-")+1);
					stat.getPositionsErrors2().put(basePosition,stat.getPositionsErrors2().get(basePosition)+1);
				}
				else
				{
					char comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(basePosition));
					stat.getMismatchErrorType().get(String.valueOf(comp)).put(String.valueOf("-"), stat.getMismatchErrorType().get(String.valueOf(comp)).get("-")+1);
					stat.getPositionsErrors2().put(basePosition,stat.getPositionsErrors2().get(basePosition)+1);
				}
				//stat.getMismatchErrorType().get(row.get(3)).put(row.get(4), stat.getMismatchErrorType().get(row.get(3)).get(row.get(4))+1);
				System.out.println("deletion 1");
				return -1;
				
				
			}
			else //if(type.getFirst()==6)
			{
				read.getCorrectedSequence().delete(basePosition, basePosition+2);
				read.getChecked().put(basePosition,true);
				read.getChecked().put(basePosition+1,true);
				if(!this.reverseFlag)
				{
					stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition))).put(String.valueOf("-"), stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition))).get("-")+1);
					stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition+1))).put(String.valueOf("-"), stat.getMismatchErrorType().get(String.valueOf(read.getCorrectedSequence().charAt(basePosition+1))).get("-")+1);
					stat.getPositionsErrors2().put(basePosition,stat.getPositionsErrors2().get(basePosition)+1);
					stat.getPositionsErrors2().put(basePosition+1,stat.getPositionsErrors2().get(basePosition+1)+1);
				}
				else
				{
					char comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(basePosition));
					stat.getMismatchErrorType().get(String.valueOf(comp)).put(String.valueOf("-"), stat.getMismatchErrorType().get(String.valueOf(comp)).get("-")+1);
					comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(basePosition+1));
					stat.getMismatchErrorType().get(String.valueOf(comp)).put(String.valueOf("-"), stat.getMismatchErrorType().get(String.valueOf(comp)).get("-")+1);
					stat.getPositionsErrors2().put(this.distance_val-basePosition,stat.getPositionsErrors2().get(this.distance_val-basePosition)+1);
					stat.getPositionsErrors2().put(this.distance_val-basePosition+1,stat.getPositionsErrors2().get(this.distance_val-basePosition+1)+1);
				}
				System.out.println("deletion 2");
				return -2;
			}
		}
		else
		{
			if(type.getFirst()<5)
			{
				char correctedBase=getMismatch(type.getFirst());
				read.getCorrectedSequence().insert(basePosition, correctedBase);
				read.getChecked().put(basePosition,true);
				read.getChecked().put(basePosition+1,true);
				read.getChecked().put(basePosition+2,true);
				if(!this.reverseFlag)
				{
					stat.getInsertFreq().put(String.valueOf(correctedBase), stat.getInsertFreq().get(String.valueOf(correctedBase))+1);
					stat.getPositionsErrors2().put(basePosition,stat.getPositionsErrors2().get(basePosition)+1);
				}
				else
				{
					char comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(correctedBase));
					stat.getInsertFreq().put(String.valueOf(comp), stat.getInsertFreq().get(String.valueOf(comp))+1);
					System.out.println("correctRead : "+ stat.getPositionsErrors2().toString()+"\n"+this.distance_val+" "+basePosition);
					stat.getPositionsErrors2().put(this.distance_val-basePosition,stat.getPositionsErrors2().get(this.distance_val-basePosition)+1);
				}

				System.out.println("inserations 1");
				return 1;
			}
			else
			{
				char charArray[] = doubleInserts(type.getFirst());
				read.getCorrectedSequence().insert(basePosition, charArray);
				read.getChecked().put(basePosition,true);
				read.getChecked().put(basePosition+1,true);
				read.getChecked().put(basePosition+2,true);
				read.getChecked().put(basePosition+3,true);
				if(!this.reverseFlag)
				{
					for(int k =0; k< charArray.length;++k)
					{
						stat.getInsertFreq().put(String.valueOf(charArray[k]), stat.getInsertFreq().get(String.valueOf(charArray[k]))+1);
						stat.getPositionsErrors2().put(basePosition+k,stat.getPositionsErrors2().get(basePosition+k)+1);
					}
				}
				else
				{
					for(int k =0; k< charArray.length;++k)
					{
						char comp = sequenceUtils.complement(read.getCorrectedSequence().charAt(charArray[k]));
						stat.getInsertFreq().put(String.valueOf(comp), stat.getInsertFreq().get(String.valueOf(comp))+1);
						stat.getPositionsErrors2().put(this.distance_val-basePosition+k,stat.getPositionsErrors2().get(this.distance_val-basePosition+k)+1);
					}
				}
				return 2;
			}
		}			
	}

	private int correctRead(int basePosition, List<PacBioTraceBackObject> path,PacBioReadContainer read) throws Exception
	{
		int frameShiftSum =0;
		
		for(int i=0; i < path.size();++i)
		{
			//frameShift 
			frameShiftSum+=correctRead(basePosition,path.get(i).getPathEntry(),read); //jump auf die richtige Position... ist nicht gegeben
			//++basePosition;
			//int test = path.get(i).getBeginPosition();
			//basePosition=basePosition+1;
			//basePosition=basePosition+1+path.get(i).getBeginPosition();
			basePosition=basePosition+path.get(i).getBeginPosition();
		}
		return frameShiftSum;
	}
	private HelperObject multipleSolution1(List<Integer>kmerValues,int kmer_count,Pair<List<Integer>, Double> insertList,int refBase, List<Integer>bestSolutions)
	{
		int minimum = 0;
		
		List<Integer> probVektor = new ArrayList<Integer>();
		for(int i=0; i< kmerValues.size();++i)
		{
			probVektor.add(Math.abs(kmer_count-kmerValues.get(i)));
		}
		//get the maximum
		for(int i=0; i < probVektor.size();++i)
		{
			if(minimum>probVektor.get(i))
			{
				minimum = probVektor.get(i);
			}
		}
		if(insertList.getSecond()<minimum)
		{
			return helperFunctionPrev(insertList);
		}
		else
		{	//check if we have multiple maxims
			//bestSolutions = new ArrayList<Integer>();
			for(int i=0; i < probVektor.size();++i)
			{
				if(minimum ==probVektor.get(i) && minimum >0)
				{
					bestSolutions.add(i);
				}
			}
			return helperOrganisingfunctionAfter(minimum, bestSolutions, refBase, insertList,mode);
		}
	}
		

	private HelperObject multipleSolution2(List<Integer>kmerValues,int kmer_count,Pair<List<Integer>, Double> insertList,int refBase, List<Integer>bestSolutions)
	{
		double maximum = 0;
		
		List<Double> probVektor = new ArrayList<Double>();
		for(int i=0; i< kmerValues.size();++i)
		{
			probVektor.add(Math.log(this.table.getValue(kmer_count,kmerValues.get(i))));
		}
		//get the maximum
		for(int i=0; i < probVektor.size();++i)
		{
			if(maximum<probVektor.get(i))
			{
				maximum = probVektor.get(i);
			}
		}
		if(insertList.getSecond()>maximum)
		{
			return helperFunctionPrev(insertList);
		}
		else
		{	//check if we have multiple maxims
			//bestSolutions = new ArrayList<Integer>();
			for(int i=0; i < probVektor.size();++i)
			{
				if(maximum ==probVektor.get(i) && maximum >0)
				{
					bestSolutions.add(i);
				}
			}
			return helperOrganisingfunctionAfter(maximum, bestSolutions, refBase, insertList,mode);
		}
	}

	private <T> HelperObject helperOrganisingfunctionAfter(T maximum,List<Integer> bestSolutions,int refBase, Pair<List<Integer>, Double> insertList,int mode)
	{
		HelperObject helperObject = new HelperObject();
		helperObject.kmerValuesMaximum=bestSolutions;
		if(mode==0||mode==1)
		{
			helperObject.kmerMaximum=(Integer) maximum;
		}
		else
		{
			helperObject.kmerDoubleMaxi=(Double)maximum;	
		}		
		helperObject.insertValuesMaximum = new ArrayList<Integer>();
		helperObject.insertMaximum=0.0;
		boolean containsRefBase = bestSolutions.contains(refBase);
		if(!containsRefBase && insertList.getSecond()==maximum)
		{				
			helperObject.insertValuesMaximum = insertList.getFirst();
			helperObject.insertMaximum = insertList.getSecond();
			helperObject.signal= new Pair<Boolean, Integer>(true,1);
			return helperObject;
		}
		else if(bestSolutions.size()>1)
		{
			if(containsRefBase)
			{
				List<Integer> newList = new ArrayList<Integer>();
				newList.add(refBase);
				helperObject.kmerValuesMaximum=newList;
				helperObject.signal=new Pair<Boolean, Integer>(false,2);
				return helperObject;
			}
			helperObject.signal= new Pair<Boolean, Integer>(true,2);
			return helperObject;
		}
		else
		{
			helperObject.signal=new Pair<Boolean, Integer>(false,2);
			return helperObject;
		}
	}
	
	private HelperObject helperFunctionPrev(Pair<List<Integer>, Double> insertList)
	{
		HelperObject helperObject = new HelperObject();
		helperObject.insertValuesMaximum = insertList.getFirst();
		helperObject.insertMaximum = insertList.getSecond();
		if(insertList.getFirst().size()>1)
		{
			
			helperObject.signal = new Pair<Boolean, Integer>(true,0); 
			return helperObject;
		}
		else
		{
			helperObject.signal = new Pair<Boolean, Integer>(false,0);
			return helperObject;
		}
	}
	private HelperObject multipleSolution0(List<Integer>kmerValues,int kmer_count,Pair<List<Integer>, Double> insertList,int refBase, List<Integer>bestSolutions)
	{
		int maximum = 0;
		//get the maximum
		for(int i=0; i < kmerValues.size();++i)
		{
			if(maximum<kmerValues.get(i))
			{
				maximum = kmerValues.get(i);
			}
		}
		if(insertList.getSecond()>maximum)
		{
			return helperFunctionPrev(insertList);
		}
		else
		{	//check if we have multiple maxims
			//bestSolutions = new ArrayList<Integer>();
			for(int i=0; i < kmerValues.size();++i)
			{
				if(maximum ==kmerValues.get(i) && maximum >0)
				{
					bestSolutions.add(i);
				}
			}
			return helperOrganisingfunctionAfter(maximum, bestSolutions, refBase, insertList,mode);
		}	
	}

	private HelperObject multipleSolution(List<Integer>kmerValues,int kmer_count,Pair<List<Integer>, Double> insertList,int refBase, List<Integer>bestSolutions)
	{		
		if(mode==0)
		{
			return multipleSolution0(kmerValues, kmer_count, insertList, refBase, bestSolutions);
		}
		else if(mode==1)
		{
			return multipleSolution1(kmerValues, kmer_count, insertList, refBase, bestSolutions);
		}
		else 
		{
			return multipleSolution2(kmerValues, kmer_count, insertList, refBase, bestSolutions);
		}
	}
	
	
	
	private double averageValuePath(List<PacBioTraceBackObject> path)
	{
		double sum=0.0;
		for(int i=0; i < path.size();++i)
		{
			sum+=path.get(i).getValue();
		}
		if(sum==0)
			return 0;
		else
			return sum/path.size();
	}
	
	private List<PacBioTraceBackObject> getTheBest1(List<List<PacBioTraceBackObject> > input)
	{
		double average=1;
		List<List<PacBioTraceBackObject>>index= new ArrayList<List<PacBioTraceBackObject>>();
		for(int i=0; i < input.size();++i)
		{
			double nAV = averageValuePath(input.get(i)); 
			if(average>nAV)
			{
				average=nAV;
				if(index.size()>1)
				{
					index.clear();
				}
				index.add(input.get(i));
			}
			else if(average==nAV)
			{
				index.add(input.get(i));
			}
		}
		if(index.size()>1)
		{
			//random choice
			return index.get(generator.nextInt(index.size()));
		}
		else
			return index.get(0);
	}
	
	private List<PacBioTraceBackObject> getTheBest2(List<List<PacBioTraceBackObject> > input)
	{
		List<Double> probValues = new ArrayList<Double>();
		for(int i=0; i < input.size();++i)
		{
			double result=0.0;
			for(int j=0; j < input.get(i).size();++j)
			{
				result+=input.get(i).get(j).getValue();
			}
			probValues.add(result);
		}
		double maximum = 0.0;
		for(int i=0; i < probValues.size();++i)
		{
			if(maximum<probValues.get(i))
			{
				maximum=probValues.get(i);
			}
		}
		List<Integer>prob2Values= new ArrayList<Integer>();
		for(int i=0; i < probValues.size();++i)
		{
			if(maximum==probValues.get(i))
			{
				prob2Values.add(i);
			}
		}
		if(prob2Values.size()>1)
		{
			return input.get(prob2Values.get(generator.nextInt(prob2Values.size())));
		}
		else
		{
			return input.get(prob2Values.get(0));
		}
	}
	
	private List<PacBioTraceBackObject> getTheBest0(List<List<PacBioTraceBackObject> > input)
	{
		double average=-1;
		List<List<PacBioTraceBackObject>>index= new ArrayList<List<PacBioTraceBackObject>>();
		for(int i=0; i < input.size();++i)
		{
			double nAV = averageValuePath(input.get(i)); 
			if(average<nAV)
			{
				average=nAV;
				if(index.size()>1)
				{
					index.clear();
				}
				index.add(input.get(i));
			}
			else if(average==nAV)
			{
				index.add(input.get(i));
			}
		}
		if(index.size()>1)
		{
			//random choice
			return index.get(generator.nextInt(index.size()));
		}
		else
			return index.get(0);
	}
	
	
	private List<PacBioTraceBackObject>getTheBest(List<List<PacBioTraceBackObject> > input)
	{
		if(mode==0)
		{
			return getTheBest0(input);
		}
		else if (mode==2)
		{
			return getTheBest2(input);
		}
		else return getTheBest1(input);
	}

	private List<PacBioTraceBackObject> recusiveFunction(int beginSeqPosition,int endSeqPosition, int kmerValue,boolean insert,int ref,PacBioReadContainer read,List<PacBioTraceBackObject> path,StringBuilder kmer,double value)
	{ 
		path.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(kmerValue,insert), value));
		if(path.size()>=3)
		{
			return path;
		}
		else
		{
			try 
			{
				PacBioKmerHelperObject nextKmer= simulateNewKmer(beginSeqPosition, endSeqPosition, kmerValue, insert, read,kmer);//Pair<StringBuilder,Integer> nextKmer = simulateNewKmer(beginSeqPosition, endSeqPosition, kmerValue, insert, read,kmer);
				if(nextKmer.getFirst().toString().contains(" "))
				{
					return path;
				}
				else
				{
					List<Integer> kmerValues = new ArrayList<Integer>();
					int refBase = originalSignal(nextKmer.getFirst());
					Pair<List<Integer>, Double> insertType = new Pair<List<Integer>, Double>();
					//we have to check now every case -> save results in kmerValues
					checkAllCases(nextKmer.getFirst(),nextKmer.getPrev_count(), nextKmer.getSecond(),read,insertType,kmerValues);
					List<Integer>bestSolutions= new ArrayList<Integer>();
					HelperObject helperObject = multipleSolution(kmerValues,nextKmer.getPrev_count(),insertType,refBase, bestSolutions);
					//check if all outcomes are 0
					if((helperObject.insertValuesMaximum == null && helperObject.kmerValuesMaximum == null) || helperObject.insertValuesMaximum.size() == 0 && helperObject.kmerValuesMaximum.size()==0)
					{
						return path;
					}
					
					Pair<Boolean,Integer> multipleOutcome =  helperObject.signal;
					double valueOutcome=-1;
					if(!multipleOutcome.getFirst())
					{
						//add outcome to the path
						Pair<Integer,Boolean> newPathEntry = null;
						if(multipleOutcome.getSecond()==0)
						{
							newPathEntry= new Pair<Integer, Boolean>(insertType.getFirst().get(0),true);
							valueOutcome= helperObject.insertMaximum;
						}
						else 
						{
							newPathEntry= new Pair<Integer, Boolean>(helperObject.kmerValuesMaximum.get(0),false);
							if(mode==0 || mode==1)valueOutcome=helperObject.kmerMaximum;
							else valueOutcome=helperObject.kmerDoubleMaxi;
						}
						path.add(new PacBioTraceBackObject(newPathEntry, valueOutcome));
						return path;
					}
					else
					{
						List<PacBioTraceBackObject> pseudopath = null;
						List<List<PacBioTraceBackObject>> results = new ArrayList<List<PacBioTraceBackObject>>();
						if(multipleOutcome.getSecond()==0) //insert have a higher score 
						{							
							for(int i=0; i < helperObject.insertValuesMaximum.size();++i)
							{
								pseudopath = new ArrayList<PacBioTraceBackObject>(path);
								results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition, helperObject.insertValuesMaximum.get(i),true,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.insertMaximum));
							}
							//search for the best one, if we find more than two -> random choice
							
						}
						else if(multipleOutcome.getSecond()==2)
						{
							for(int i=0; i < helperObject.kmerValuesMaximum.size();++i)
							{
								pseudopath = new ArrayList<PacBioTraceBackObject>(path);
								if(mode==1 || mode==0)
								results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition,helperObject.kmerValuesMaximum.get(i),false,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.kmerMaximum));
								else results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition,helperObject.kmerValuesMaximum.get(i),false,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.kmerDoubleMaxi));
									
							}
						}
						else
						{
							for(int i=0; i < helperObject.insertValuesMaximum.size();++i)
							{
								pseudopath = new ArrayList<PacBioTraceBackObject>(path);
								results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition, helperObject.insertValuesMaximum.get(i),true,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.insertMaximum));
							}
							for(int i=0; i < helperObject.kmerValuesMaximum.size();++i)
							{
								pseudopath = new ArrayList<PacBioTraceBackObject>(path);
								if(mode==0|| mode==1)
								results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition,helperObject.kmerValuesMaximum.get(i),false,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.kmerMaximum));
								else results.add(recusiveFunction(nextKmer.getSecond(),endSeqPosition,helperObject.kmerValuesMaximum.get(i),false,refBase,read,pseudopath,nextKmer.getFirst(),helperObject.kmerDoubleMaxi));
							}
						}
						return getTheBest(results);						
					}
				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
		return null;
	}
	
	private List<PacBioTraceBackObject>getMaximumPrev(int beginPosition,int endIndex,StringBuilder kmer,PacBioReadContainer read,Pair<List<Integer>,Double>insertType,int refBase)
	{
		if(insertType.getFirst().size()>1)
		{
			List<List<PacBioTraceBackObject>> results = new ArrayList<List<PacBioTraceBackObject>>();
			for(int i=0; i < insertType.getFirst().size();++i)
			{
				List<PacBioTraceBackObject> path = new ArrayList<PacBioTraceBackObject>();
				//here we have to check the next kmer ->rekusive call until one breaks down (3 more steps are allowed)
				results.add(recusiveFunction(beginPosition,endIndex,insertType.getFirst().get(i),true,refBase,read,path,kmer,insertType.getSecond())); //check the next kmer last kmer with inserts
				//return maxValues.get(generator.nextInt(5));
				//totalpath.add(path);
			}				
			return getTheBest(results);
		}
		else
		{
			List<PacBioTraceBackObject> singleEntry = new ArrayList<PacBioTraceBackObject>();
			singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(insertType.getFirst().get(0), true),insertType.getSecond()));
			return singleEntry;
		}
	}
	
	private <T> List<PacBioTraceBackObject> getMaximumAfter(T maximum_deletionMismatch,List<Integer> maxValues,int beginPosition,int endIndex,StringBuilder kmer,PacBioReadContainer read,Pair<List<Integer>,Double>insertType,int refBase)
	{
		boolean containsRefBase =  maxValues.contains(refBase);
		boolean containsDeletion = maxValues.contains(5);
		boolean containsDeletionType2 = maxValues.contains(6);
		
		if(!containsDeletion && !containsDeletion && !containsRefBase && insertType.getSecond()== maximum_deletionMismatch)
		{
			List<List<PacBioTraceBackObject>> results = new ArrayList<List<PacBioTraceBackObject>>();
			for(int i=0; i < insertType.getFirst().size();++i)
			{
				List<PacBioTraceBackObject> path = new ArrayList<PacBioTraceBackObject>();
				results.add(recusiveFunction(beginPosition,endIndex,insertType.getFirst().get(i),true,refBase,read,path,kmer,insertType.getSecond())); //check the next kmer last kmer with inserts
			}
			for(int i=0; i < maxValues.size();++i)
			{
				List<PacBioTraceBackObject> path = new ArrayList<PacBioTraceBackObject>();
				results.add(recusiveFunction(beginPosition,endIndex,maxValues.get(i),false,refBase,read,path,kmer,(Double) maximum_deletionMismatch)); //check the next kmer, last kmer was without inserts
			}
			
			return getTheBest(results);	
		}
		else if(!containsRefBase && insertType.getSecond()== maximum_deletionMismatch)
		{
			List<List<PacBioTraceBackObject>> results = new ArrayList<List<PacBioTraceBackObject>>();
			for(int i=0; i < insertType.getFirst().size();++i)
			{
				List<PacBioTraceBackObject> path = new ArrayList<PacBioTraceBackObject>();
				results.add(recusiveFunction(beginPosition,endIndex,insertType.getFirst().get(i),true,refBase,read,path,kmer,insertType.getSecond())); //check the next kmer last kmer with inserts
			}
			List<PacBioTraceBackObject> singleEntry = new ArrayList<PacBioTraceBackObject>();
			
			if(containsDeletion)
			{
				singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(5,false),(Double) maximum_deletionMismatch));
				results.add(singleEntry);
			}
			
			if(containsDeletionType2)
			{
				singleEntry.clear();
				singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(6,false),(Double) maximum_deletionMismatch));
				results.add(singleEntry);
			}
			
			return getTheBest(results);
		}
		else if(maxValues.size()>1) //now we have to decide
		{
			//first check if the original is the maximum
			if(containsRefBase)
			{
				List<PacBioTraceBackObject> singleEntry = new ArrayList<PacBioTraceBackObject>();
				singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(refBase,false),(Double) maximum_deletionMismatch));
				return singleEntry;
			}
			else if(containsDeletion || containsDeletionType2)
			{
				List<PacBioTraceBackObject> singleEntry = new ArrayList<PacBioTraceBackObject>();
				if(containsDeletion)
				{
					singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(5,false),(Double)maximum_deletionMismatch));	
				}
				else
				{
					singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(6,false),(Double)maximum_deletionMismatch));
				}
				return singleEntry;
			}
			else //next kmer check! recusion
			{
				List<List<PacBioTraceBackObject>> results = new ArrayList<List<PacBioTraceBackObject>>();
				for(int i=0; i < maxValues.size();++i)
				{
					List<PacBioTraceBackObject> path = new ArrayList<PacBioTraceBackObject>();
					results.add(recusiveFunction(beginPosition,endIndex,maxValues.get(i),false,refBase,read,path,kmer,(Double)maximum_deletionMismatch)); //check the next kmer, last kmer was without inserts
				}
				return getTheBest(results);	
			}			
		}
		else
		{
			List<PacBioTraceBackObject> singleEntry = new ArrayList<PacBioTraceBackObject>();
			singleEntry.add(new PacBioTraceBackObject(new Pair<Integer, Boolean>(maxValues.get(0),false),(Double)maximum_deletionMismatch));
			return singleEntry;
		}
	}
	
	
	private List<PacBioTraceBackObject> getMaximum1(int beginPosition,int kmer_count, int endIndex,StringBuilder kmer,PacBioReadContainer read,List<Integer> kmerValues,Pair<List<Integer>,Double>insertType,int refBase)
	{
		double minimum = Double.MAX_VALUE;
		
		List<Integer> probVektor = new ArrayList<Integer>();
		for(int i=0; i< kmerValues.size();++i)
		{
			probVektor.add(Math.abs(kmer_count-kmerValues.get(i)));
		}
		//get the maximum
		for(int i=0; i < probVektor.size();++i)
		{
			if(minimum>probVektor.get(i))
			{
				minimum = probVektor.get(i);
			}
		}
		if(insertType.getSecond()< minimum && insertType.getFirst().size()>0)
		{
			return getMaximumPrev(beginPosition, endIndex, kmer, read, insertType, refBase);
		}
		else
		{
			List<Integer> maxValues = new ArrayList<Integer>();
			for(int i=0; i < probVektor.size();++i)
			{
				if(minimum == probVektor.get(i))
				{
					maxValues.add(i);
				}
			}
			return getMaximumAfter(minimum, maxValues, beginPosition, endIndex, kmer, read, insertType, refBase);
		}			
	}
	
	
	private List<PacBioTraceBackObject> getMaximum2(int beginPosition,int kmer_count, int endIndex,StringBuilder kmer,PacBioReadContainer read,List<Integer> kmerValues,Pair<List<Integer>,Double>insertType,int refBase)
	{
		double maximum = 0;
		
		List<Double> probVektor = new ArrayList<Double>();
		for(int i=0; i< kmerValues.size();++i)
		{
			probVektor.add(Math.log(this.table.getValue(kmer_count,kmerValues.get(i))));
		}
		//get the maximum
		for(int i=0; i < probVektor.size();++i)
		{
			if(maximum<probVektor.get(i))
			{
				maximum = probVektor.get(i);
			}
		}
		if(insertType.getSecond()> maximum)
		{
			return getMaximumPrev(beginPosition, endIndex, kmer, read, insertType, refBase);
		}
		else
		{
			List<Integer> maxValues = new ArrayList<Integer>();
			for(int i=0; i < kmerValues.size();++i)
			{
				if(maximum == probVektor.get(i))
				{
					maxValues.add(i);
				}
			}
			return getMaximumAfter(maximum, maxValues, beginPosition, endIndex, kmer, read, insertType, refBase);
		}
	}	
	
	private List<PacBioTraceBackObject> getMaximum0(int beginPosition, int kmer_count,int endIndex,StringBuilder kmer,PacBioReadContainer read,List<Integer> kmerValues,Pair<List<Integer>,Double>insertType,int refBase)
	{
		double maximum_deletionMismatch = 0;
		//get the maximum
		for(int i=0; i < kmerValues.size();++i)
		{
			if(maximum_deletionMismatch<kmerValues.get(i))
			{
				maximum_deletionMismatch = kmerValues.get(i);
			}
		} 
		if(insertType.getSecond()> maximum_deletionMismatch)
		{
			return getMaximumPrev(beginPosition, endIndex, kmer, read, insertType, refBase);
		}
		else
		{
			//check if we have multiple maxims
			List<Integer> maxValues = new ArrayList<Integer>();
			for(int i=0; i < kmerValues.size();++i)
			{
				if(maximum_deletionMismatch == kmerValues.get(i))
				{
					maxValues.add(i);
				}
			}
			if(maxValues.size()==0)
			{
				System.out.print(kmerValues.toString());
				System.out.print(insertType.getSecond());
			}
			return getMaximumAfter(maximum_deletionMismatch, maxValues, beginPosition, endIndex, kmer, read, insertType, refBase);			
		}	
	}
	
	
	private List<PacBioTraceBackObject> getMaximum(int beginPosition,int kmer_count, int endIndex,StringBuilder kmer,PacBioReadContainer read,List<Integer> kmerValues,Pair<List<Integer>,Double>insertType,int refBase)
	{
		if(this.mode==0)
		{
			return getMaximum0(beginPosition, kmer_count, endIndex, kmer, read, kmerValues, insertType, refBase);
		}
		else if(this.mode==1)
		{
			return getMaximum1(beginPosition,kmer_count, endIndex, kmer, read, kmerValues, insertType, refBase);
		}
		else
			return getMaximum2(beginPosition,kmer_count, endIndex, kmer, read, kmerValues, insertType, refBase);
	}
	
	
	private Pair<List<Integer>,Double> getInsertFitting(List<Integer>kmerValues, int kmer_count)
	{
		//smallest difference
		double minimum =Integer.MAX_VALUE;
		for(int i=0; i < kmerValues.size();++i)
		{
			if(kmerValues.get(i)==0)
			{
				continue;
			}
			double temp =Math.abs(kmer_count-kmerValues.get(i));
			if(temp<minimum)
			{
				minimum=temp;
			}
		}
		List<Integer> maxValues = new ArrayList<Integer>();
		
		if(minimum<Integer.MAX_VALUE)
		{
			for(int i=0; i < kmerValues.size();++i)
			{
				if(minimum ==Math.abs(kmer_count-kmerValues.get(i)))
				{
					maxValues.add(i);
				}
			}
		}	
		else  //cheating
		{
			maxValues.add(0);
		}
		return new Pair<List<Integer>, Double>(maxValues,minimum);
	}
	
	private Pair<List<Integer>,Double> getInsertMaximaProb(List<Integer>kmerValues,int kmer_count)
	{
		double maximum = 0.0;
		for(int i=0; i < kmerValues.size();++i)
		{
			double temp = this.table.getValue(kmer_count,kmerValues.get(i));
			if(temp>maximum)
			{
				maximum=temp;
			}
		}
		List<Integer> maxValues = new ArrayList<Integer>();
		
		if(maximum>0)
		{
			for(int i=0; i < kmerValues.size();++i)
			{
				if(maximum == this.table.getValue(kmer_count,kmerValues.get(i)))
				{
					maxValues.add(i);
				}
			}
		}
		return new Pair<List<Integer>, Double>(maxValues,maximum);
	}
	
	private Pair<List<Integer>,Double> getInsertMaxima(List<Integer>kmerValues)
	{
		double maximum = 0;
		//get the maximum
		for(int i=0; i < kmerValues.size();++i)
		{
			if(maximum<kmerValues.get(i))
			{
				maximum = kmerValues.get(i);
			}
		}
		//check if we have multiple maxims
		List<Integer> maxValues = new ArrayList<Integer>();
		if(maximum>0)
		{
			for(int i=0; i < kmerValues.size();++i)
			{
				if(maximum ==kmerValues.get(i))
				{
					maxValues.add(i);
				}
			}
		}
		return new Pair<List<Integer>, Double>(maxValues,maximum);
	}
	
	private int checkKmerDistribution(String kmer)
	{
		//System.out.print(kmer+ " kmer check\n");
		if(kmer.equals(""))
		{
			if(mode==0 || mode==2)
			{
				return 0;
			}
			else 
			{
				return Integer.MAX_VALUE;
			}
		}
		String kmerToUse = kmerFunctions.kmerToUse(kmer);
		BitSet bS = ks.getUtils().stringToBitSet(kmerToUse);
		//BitSet bS = ks.getUtils().stringToBitSet(kmerFunctions.kmerToUse(kmer.toString()));
		if(ks.exists(kmerToUse))
		{
			return(ks.getNumber(bS));
		}
		else
		{
			if(mode==0 || mode==2)
			{
				return 0;
			}
			else 
			{
				return Integer.MAX_VALUE;
			}
		}
	}
	
	private Pair<List<Integer>,Double> insertion(StringBuilder originalKmer,int kmer_count, int begin, PacBioReadContainer read)
	{
		if(begin+1>= read.getCorrectedSequence().length())
		{
			List<Integer>tempList = new ArrayList<Integer>();
			return new Pair<List<Integer>, Double>(tempList,0.0);
		}
		List<Integer> tempKmerValues= new ArrayList<Integer>();
		StringBuilder kmer = null;
		for(int i=0; i < 5;++i)
		{
			kmer = new StringBuilder(originalKmer.substring(2, originalKmer.length()-1));
			if (i==0)
			{
				kmer.append('A');
			}
			else if (i==1)
			{
				kmer.append('C');
			}
			else if (i==2)
			{
				kmer.append('G');
			}
			else if (i==3)
			{
				kmer.append('T');
			}
			else if (i==4)
			{
				kmer.append('N');
			}
			kmer.append(originalKmer.charAt(originalKmer.length()-1));
			kmer.append(read.getCorrectedSequence().charAt(begin+1));
			//System.out.println("check7");
			tempKmerValues.add(checkKmerDistribution(kmer.toString()));
		}
		for(int i=0; i < 16;++i)
		{
			kmer = new StringBuilder(originalKmer.substring(3, originalKmer.length()-1));
			if(i==0)
			{
				kmer.append("AA");
			}
			else if(i==1)
			{
				kmer.append("AC");
			}
			else if(i==2)
			{
				kmer.append("AG");
			}
			else if(i==3)
			{
				kmer.append("AT");
			}
			else if(i==4)
			{
				kmer.append("CA");
			}			
			else if(i==5)
			{
				kmer.append("CC");
			}
			else if(i==6)
			{
				kmer.append("CG");
			}
			else if(i==7)
			{
				kmer.append("CT");
			}
			else if(i==8)
			{
				kmer.append("GA");
			}
			else if(i==9)
			{
				kmer.append("GC");
			}
			else if(i==10)
			{
				kmer.append("GG");
			}
			else if(i==11)
			{
				kmer.append("GT");
			}
			else if(i==12)
			{
				kmer.append("TA");
			}
			else if(i==13)
			{
				kmer.append("TC");
			}			
			else if(i==14)
			{
				kmer.append("TG");
			}
			else if(i==15)
			{
				kmer.append("TT");
			}
			kmer.append(originalKmer.charAt(originalKmer.length()-1));
			kmer.append(read.getCorrectedSequence().charAt(begin+1));
			//System.out.println("check8");
			tempKmerValues.add(checkKmerDistribution(kmer.toString()));
		}
		if(mode==0) return getInsertMaxima(tempKmerValues);//tempKmerValues.get(position);
		else if(mode==1)
		{
			return getInsertFitting(tempKmerValues,kmer_count);
		}
		else return getInsertMaximaProb(tempKmerValues,kmer_count);
	}
	
	private StringBuilder deletion(StringBuilder kmer,int begin,PacBioReadContainer read, int type)
	{
		StringBuilder newKmer = new StringBuilder(kmer.substring(1, kmer.length()-1));
		if(type==0)
		{
			if(begin+2 <read.getCorrectedSequence().length())
			{
				newKmer.append(read.getCorrectedSequence().substring(begin+1,begin+3));
			}
			else
			{
				newKmer = new StringBuilder("");
			}
		}
		else if (type==1)
		{
			if(begin+3<read.getCorrectedSequence().length())
			{
				newKmer.append(read.getCorrectedSequence().substring(begin+2,begin+4));
			}
			else
			{
				newKmer = new StringBuilder("");
			}
		}
		return newKmer;
	}
		
	private void checkAllCases(StringBuilder originalKmer,int kmer_count,int beginIndex, PacBioReadContainer read, Pair<List<Integer>,Double> insertType,List<Integer>kmerValues)
	{
		
		for(int i=0; i < 8;++i)
		{
			StringBuilder kmer = new StringBuilder(originalKmer);
			if(i==0) //correct by A
			{
				kmer.setCharAt(kmer.length()-1, 'A');
			}
			else if(i==1) //correct by C
			{
				kmer.setCharAt(kmer.length()-1, 'C');
			}
			else if(i==2) //correct by G
			{
				kmer.setCharAt(kmer.length()-1, 'G');
			}
			else if(i==3) //correct by T
			{
				kmer.setCharAt(kmer.length()-1, 'T');
			}
			else if(i==4)//correct by N
			{
				kmer.setCharAt(kmer.length()-1, 'N');
			}
			else if(i==5) //deletion 1
			{
				kmer = deletion(kmer,beginIndex,read,0);
				//check the next coming kmer
			}
			else if(i==6) //deletion 2
			{
				kmer = deletion(kmer,beginIndex,read,1);
			}
			if(i<7)
			{
				//System.out.println("check9");
				kmerValues.add(checkKmerDistribution(kmer.toString())); //<- here we looking at the maximum number of counts 
			}
			else if(i==7) //now we have to check inserts
			{
				Pair<List<Integer>, Double> temp = insertion(kmer,kmer_count,beginIndex,read); 
				insertType.setFirst(temp.getFirst());
				insertType.setSecond(temp.getSecond());
			}
		}		
	}
	
	private int originalSignal(StringBuilder kmer)
	{
		String kmerToUse = kmerFunctions.kmerToUse(kmer.toString());
		int signal =-1;
		switch (kmerToUse.charAt(kmerToUse.length()-1)) 
		{
			case 'A':
					signal=0;
					break;
			case 'C':
					signal=1;
					break;
			case 'G':
					signal=2;
					break;
			case 'T':
					signal=3;
					break;
			case 'N':
					signal=4;
					break;
			default:
					break;
		}
		return signal;
	}
	
	private int correction(StringBuilder kmer,int kmer_count ,PacBioReadContainer read,int begin,int endIndex)
	{
		List<Integer> kmerValues = new ArrayList<Integer>();
		//easy cases
		int refBase = originalSignal(kmer);
		Pair<List<Integer>, Double> insertType = new Pair<List<Integer>, Double>();
		//we have to check now every case -> save results in kmerValues
		checkAllCases(kmer,kmer_count, begin, read, insertType,kmerValues);		
		//now we have to find the best one! (-> maximum .... should be change to nearest average!)
		List<PacBioTraceBackObject> bestpath =getMaximum(begin,kmer_count,endIndex,kmer,read,kmerValues,insertType, refBase);
		try 
		{
			 return correctRead(begin,bestpath,read);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//return frameShift(bestFit);
		return -1;
	}
	 
	private void correctLastInterval(int beginIndex,int endIndex,PacBioReadContainer read)
	{
		//construct a reverse complement read
		PacBioReadContainer newReversedRead = new PacBioReadContainer();
		List<Integer>reversedNotCorrectedPositon = new ArrayList<Integer>();
		//first construct the reverse sequence
		StringBuilder reversedRead = new StringBuilder();
		int endPosition= read.getPositionsList().get(endIndex)+this.ks.getKmerSize();
		if(endPosition+1 > read.getCorrectedSequence().length())
		{
			System.out.println(" correctLastIntervall "+ " "+ endPosition+" "+read.getCorrectedSequence().length());
			endPosition= read.getCorrectedSequence().length()-1; //hmm error fix
		}
		reversedRead.append(read.getCorrectedSequence().substring(0, endPosition+1));
		reversedRead=sequenceUtils.reverseComplement2(reversedRead);
		newReversedRead.setCorrectedSequence(reversedRead);
		reversedRead = new StringBuilder();
		reversedRead.append(read.getCorrectedSequence().substring(0, endPosition+1));
		reversedRead=sequenceUtils.reverseComplement2(reversedRead);
		newReversedRead.setOrginalSequence(reversedRead.toString());
		HashMap<Integer,Boolean> newCheckedMap = new HashMap<Integer, Boolean>();
		newReversedRead.setChecked(newCheckedMap);
		
		int distance= read.getPositionsList().get(endIndex)+ks.getKmerSize();
		System.out.println("distance "+ distance);
		System.out.println("vorher "+read.getPositionsList().toString());
		//now we have to adjust the indexes
		for(int i=endIndex-1; i >=0;--i)
		{
			reversedNotCorrectedPositon.add(distance-read.getPositionsList().get(i));
		}
		newReversedRead.setNotCorrectedPositions(reversedNotCorrectedPositon);
		System.out.println("nachher "+newReversedRead.getPositionsList().toString());
		this.reverseFlag=true;
		this.distance_val=distance; //TODO wichtig für statistik
		
		correctInterval(0,reversedNotCorrectedPositon.size(),newReversedRead);  
		
		// now we have to translate everything back!
		StringBuilder newCorrectedRead = new StringBuilder();
		newCorrectedRead.append(sequenceUtils.reverseComplement2(newReversedRead.getCorrectedSequence()));
		newCorrectedRead.append(read.getCorrectedSequence().substring(endPosition,read.getCorrectedSequence().length()));
		read.setCorrectedSequence(newCorrectedRead);
	}
	
	private Integer corruptedAlignmentStructure(int beginIndex,int frameShift, int lastCorrectedPosition, PacBioReadContainer read)
	{
		boolean found = false;
		int counter=1;
		//int test_output = read.getNotCorrectedPositions().get(beginIndex)+frameShift;
		int newStartposition = read.getPositionsList().get(beginIndex)+frameShift-(this.ks.getKmerSize());
		StringBuilder kmer = new StringBuilder();
		int goodKmerBegin =0;
	//	int goodKmerEnd_c =0;
		while(!found && newStartposition-counter+1 !=read.getPositionsList().get(lastCorrectedPosition) && newStartposition-counter+1>=0)
		{
			goodKmerBegin = newStartposition-counter+1;
		//	goodKmerEnd_c= newStartposition-counter+this.ks.getKmerSize()+1;
			System.out.println("corruptedAlignment kmer building "+goodKmerBegin+" "+ (newStartposition-counter+this.ks.getKmerSize()+1) +" "+ read.getCorrectedSequence().length());
			kmer = new StringBuilder(read.getCorrectedSequence().substring(newStartposition-counter+1,newStartposition-counter+this.ks.getKmerSize()+1));
			++counter;
			if(checkKmerDistribution(kmer.toString())>0)
			{
				found=true;
			}
		}
		if(found)
		{
			//now we have to do every step until we reach our initial position
			int newFrameShift=frameShift;
			int i=1;
			while(goodKmerBegin+i<=newStartposition+1 && goodKmerBegin+i+this.ks.getKmerSize()<=read.getCorrectedSequence().length())
			{
				int test_i = goodKmerBegin+i;
				int test_j = goodKmerBegin+i+this.ks.getKmerSize();
				//boolean bla = read.getChecked().containsKey(test_j-1);
				if(read.getChecked().get(test_j-1)==null || !read.getChecked().get(test_j-1))
				{
						kmer = new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j));
						if(!this.reverseFlag)
						{
							System.out.println("stepbystep "+ kmer +" forward" + test_i +" "+" "+test_j);
						}
						else
						{
							System.out.println("stepbystep "+sequenceUtils.reverseComplement2(kmer) +" reveresed complement" + test_i +" "+" "+test_j);
						}
						//System.out.println("check10");
						//now we have to correct it and after correction we have to update the frameShift counter
						int kmer_count=checkKmerDistribution(read.getCorrectedSequence().substring(test_i-1,test_j-1));
						newFrameShift=correction(kmer,kmer_count,read,test_j-1,read.getPositionsList().get(beginIndex)+1+newFrameShift)+newFrameShift;
						if(!this.reverseFlag)
						{
							System.out.println("stepbystep result "+ new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j)) +" forward" + test_i +" "+" "+test_j);
						}
						else
						{
							System.out.println("stepbystep result "+sequenceUtils.reverseComplement2(new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j))) +" reveresed complement" + test_i +" "+" "+test_j);
						}
//						System.out.println(new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j)));
//						System.out.println(sequenceUtils.reverseComplement2(new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j))) +" reveresed complement");
				}
				++i;
			}
			return newFrameShift;
		}
		else //TODO index checking!
		{
			//step by step correction
			//go to the last kmer
			goodKmerBegin = read.getPositionsList().get(lastCorrectedPosition)-this.ks.getKmerSize();
			newStartposition=read.getPositionsList().get(beginIndex);
			int newFrameShift=frameShift;
			int i=1;
			while(goodKmerBegin+i<=newStartposition+1 && goodKmerBegin+i+this.ks.getKmerSize() <= read.getCorrectedSequence().length())
			{
				int test_i = goodKmerBegin+i;
				int test_j = goodKmerBegin+i+this.ks.getKmerSize();
				//boolean bla = read.getChecked().containsKey(test_j-1);
				if(read.getChecked().get(test_j-1)==null || !read.getChecked().get(test_j-1))
				{
						kmer = new StringBuilder(read.getCorrectedSequence().substring(test_i,test_j));
						//now we have to correct it and after correction we have to update the frameShift counter
						//System.out.println("check11");
						int kmer_count=checkKmerDistribution(read.getCorrectedSequence().substring(test_i-1,test_j-1));
						newFrameShift=correction(kmer,kmer_count,read,test_j-1,read.getPositionsList().get(beginIndex)+1+newFrameShift)+newFrameShift;	
				}
				++i;
			}
			return newFrameShift;
		}
		
	}
	private void putIntoMapSave(HashMap<Integer, Integer> mapA, int value)
	{
		if(mapA.get(value)!=null)
		{
			mapA.put(value,mapA.get(value)+1);
		}
		else
		{
			mapA.put(value,1);
		}
	}
	
	
	private int findLastCorrectedPosition(PacBioReadContainer read, int beginIndex)
	{
		System.out.println("beginIndex " + beginIndex);
		System.out.println(read.getPositionsList().toString());
		System.out.println(read.getPositionsList().get(beginIndex));
		System.out.println("find last corrected Position \n" + read.getChecked().toString());
		
		for(int i=beginIndex; i >=0;--i)
		{
			if(read.getChecked().get(read.getPositionsList().get(i))!=null)
			{
				return i;
			}
		}
		
		return 0;
	}

	private void correctInterval(int beginIndex, int endIndex,PacBioReadContainer read)
	{
		//get the substring
		int frameShift=0;
		for(int i=beginIndex; i <endIndex;++i)
		{

				int correctSeqPosition = read.getPositionsList().get(i)+frameShift; //todo framshift könnte problem auslösen
				if(correctSeqPosition>read.getCorrectedSequence().length())
				{
					break;
				}
				//check if we have to correct the position anymore
				if(read.getChecked().get(correctSeqPosition)==null || !read.getChecked().get(correctSeqPosition))
				{
					int correctEndSeqPosition = 0;
					if(endIndex== read.getPositionsList().size())
					{
						correctEndSeqPosition =read.getPositionsList().get(endIndex-1)+1+frameShift;
					}
					else
					{
						correctEndSeqPosition =read.getPositionsList().get(endIndex)+frameShift;
					}
					//check if we are able to find the last kmer( if the width is high enough)
					StringBuilder kmer = new StringBuilder(read.getCorrectedSequence().substring(correctSeqPosition-(this.ks.getKmerSize()), correctSeqPosition));
					if(checkKmerDistribution(kmer.toString())==0) //&&distance high enough
					{
						//now we have to find the correct seed //beginBorder is the last entry which has not been corrected 
						int lastCorrectedPosition=findLastCorrectedPosition(read, i);
						frameShift=corruptedAlignmentStructure(i, frameShift, lastCorrectedPosition, read);
					}
					else
					{
						System.out.println("building kmer: " +read.getCorrectedSequence().length()+" "+ (correctSeqPosition-(this.ks.getKmerSize()-1))+" " + (correctSeqPosition+1) );
						System.out.println(kmer +" forward "+ (correctSeqPosition-(this.ks.getKmerSize()-1) +" "+ correctSeqPosition));
						//add+frameShift
						if(correctSeqPosition== read.getCorrectedSequence().length())
						{
							correctSeqPosition = correctSeqPosition-1;
						}
						//System.out.println("check0");
						this.start_kmer_count=checkKmerDistribution(kmer.toString());
						//either here we have to look at the enviourment
						kmer = new StringBuilder(read.getCorrectedSequence().substring(correctSeqPosition-(this.ks.getKmerSize()-1),correctSeqPosition+1));
						//now we have to correct it and after correction we have to update the frameShift counter
						//frameShift=correction(kmer,read,correctSeqPosition,correctEndSeqPosition);
						frameShift=correction(kmer,this.start_kmer_count,read,correctSeqPosition,correctEndSeqPosition);
						if(!this.reverseFlag)
						{
							System.out.println("normal " +new StringBuilder(read.getCorrectedSequence().substring(correctSeqPosition-(this.ks.getKmerSize()-1),correctSeqPosition+1)));
						}
						else
						{
							System.out.println("normal "+sequenceUtils.reverseComplement2(new StringBuilder(read.getCorrectedSequence().substring(correctSeqPosition-(this.ks.getKmerSize()-1),correctSeqPosition+1))) +" reveresed complement");
						}
	//					read.getFrameShifts().set(i,frameShift);
					}
			}
			else
			{
				continue;				
			}
		}
	}
	
	public PacBioReadContainer correctReadByKmers(PacBioReadContainer read)
	{
		//first check if we are able to correct it if yes get the last kmer
		PacBioKmerCheck checkKmers = new PacBioKmerCheck(); //returns a list of valid kmers
		List<Integer> validKmers=checkKmers.checkForKmer(read);
		if(validKmers.size()==0)
		{
			boolean flag= new PacBioSeedFinding(read,ks).findSeed();
			if(!flag) return read;
			else
			{
				validKmers=checkKmers.checkForKmer(read);
			}
		}
		List<Integer> notCorrectedPosition = read.getPositionsList();
//		List<Integer> frameShifts = new ArrayList<Integer>();
//		for(int i=0; i < read.getNotCorrectedPositions().size();++i)
//		{
//			frameShifts.add(0);
//		}
//		read.setFrameShifts(frameShifts);
//		frameShifts=null;
		this.reverseFlag=false;
		for(int i=validKmers.size()-1;i>=0;--i) 
		{
			if(i==validKmers.size()-1)
			{
				
				correctInterval(validKmers.get(i),notCorrectedPosition.size(),read);  
			}
			else
			{
				correctInterval(validKmers.get(i),validKmers.get(i+1),read);
			}
		}
		correctLastInterval(0,validKmers.get(0),read);
		return read;
	}
}
